/*++

Copyright (c) 1989 - 1999 Microsoft Corporation

Module Name:

    netroot.c

Abstract:

    This module implements the routines for creating the net root.

--*/

#include "precomp.h"
#pragma hdrstop

#include "mrxglobs.h"
#include "host_access.h"

//
// Forward declarations ...
//

NTSTATUS
NullMiniInitializeNetRootEntry(
    IN PMRX_NET_ROOT pNetRoot
    );

NTSTATUS
HwFsUpdateNetRootState(
    IN OUT PMRX_NET_ROOT pNetRoot)
/*++

Routine Description:

   This routine updates the mini redirector state associated with a net root.

Arguments:

    pNetRoot - the net root instance.

Return Value:

    NTSTATUS - The return status for the operation

Notes:

    By diffrentiating the mini redirector state from the net root condition it is possible
    to permit a variety of reconnect strategies. It is conceivable that the RDBSS considers
    a net root to be good while the underlying mini redirector might mark it as invalid
    and reconnect on the fly.

--*/
{
    NTSTATUS Status = STATUS_NOT_IMPLEMENTED;

    HWFS_DBG_PRINT(DBG_INFO, DBG_SESSION, ("HwFsUpdateNetRootState\n"));
    return(Status);
}

// Compare RootName with PatternName
BOOLEAN
CheckRootName(
    PWCHAR    pRootName,
    USHORT    RootNameLength,
    PWCHAR    pPatternName,
    USHORT    PatternNameLength)
{
    const ULONG PatternNameChars = PatternNameLength / sizeof(WCHAR);
    ULONG RootNameChars = RootNameLength / sizeof(WCHAR);
    if (RootNameChars < PatternNameChars) {
        return FALSE;
    }

    if (0 != _wcsnicmp(pRootName, pPatternName, PatternNameChars)) {
        return FALSE;
    }

    if (RootNameChars == PatternNameChars) {
        return TRUE;
    }
    return pRootName[PatternNameChars] == L'\\' || pRootName[PatternNameChars] == 0;
}

NTSTATUS
HwFsCreateVNetRoot(
    IN PMRX_CREATENETROOT_CONTEXT pCreateNetRootContext
    )
{
    // The MRxCreateVNetRoot routine is called by RDBSS to request that the network mini-redirector
    // create a V_NET_ROOT structure and, in some cases, a NET_ROOT structure.
    NTSTATUS        Status;
    PRX_CONTEXT     pRxContext = pCreateNetRootContext->RxContext;
    PMRX_V_NET_ROOT pVNetRoot = (PMRX_V_NET_ROOT)pCreateNetRootContext->pVNetRoot;
    PHWFS_NETROOT_EXTENSION  pNetRootExt;
    PHWFS_DEVICE_EXTENSION   pDevExt;

    PMRX_SRV_CALL   pSrvCall;
    PMRX_NET_ROOT   pNetRoot;

    BOOLEAN Verifyer = FALSE;
    BOOLEAN  fTreeConnectOpen = TRUE; // RxContext->Create.ThisIsATreeConnectOpen;
    BOOLEAN  fInitializeNetRoot;

    pNetRoot = pVNetRoot->pNetRoot;
    pSrvCall = pNetRoot->pSrvCall;

    pDevExt = HWFS_GET_DEVICE_EXTENSION(pRxContext);

    pNetRootExt = pNetRoot->Context;

    HWFS_DBG_PRINT(DBG_INFO, DBG_SESSION,
        ("HwFsCreateVNetRoot type %08lx; RootName %wZ, SrvCallName %wZ; Initialized %d, ShareConnected %d\n",
        pNetRoot->Type, pNetRoot->pNetRootName, pSrvCall->pSrvCallName,
        pNetRootExt->fInitialized, HostShareIsConnected(&pNetRootExt->Share)));

    if (pNetRoot->Type == NET_ROOT_MAILSLOT) {
        Status = STATUS_NOT_SUPPORTED;
        goto done;
    }

    ASSERT(NodeType(pNetRoot) == RDBSS_NTC_NETROOT);
    ASSERT(NodeType(pNetRoot->pSrvCall) == RDBSS_NTC_SRVCALL);

    if (pNetRoot->pNetRootName->Length < pSrvCall->pSrvCallName->Length) {
        Status = STATUS_BAD_NETWORK_NAME;
        goto done;
    }

    USHORT rootNameLength = pNetRoot->pNetRootName->Length - pSrvCall->pSrvCallName->Length;
    PWCHAR pRootName = (PWCHAR)(pNetRoot->pNetRootName->Buffer +
        (pSrvCall->pSrvCallName->Length / sizeof(WCHAR)));

    if (rootNameLength < sizeof(WCHAR) || pRootName[0] != L'\\') {
        Status = STATUS_BAD_NETWORK_NAME;
        goto done;
    }

    if (!pNetRootExt->fInitialized) {
        RxDefineNode(pNetRootExt, HWFS_NETROOT_EXTENSION);
        HostInitShareStruct(&pNetRootExt->Share);

        pNetRootExt->RootName.MaximumLength = pNetRootExt->RootName.Length =
            rootNameLength - sizeof(WCHAR);
        pNetRootExt->RootName.Buffer = pRootName + 1; // skip leading '\\'

        pNetRootExt->fInitialized = TRUE;
    }

#define IPC_NAME L"\\IPC$"
#define IPC_NAME_SIZE (sizeof(IPC_NAME) - sizeof(WCHAR))

    //  \IPC$[\*] case processing
    if (pNetRoot->Type == NET_ROOT_PIPE &&
        CheckRootName(pRootName, rootNameLength, IPC_NAME, IPC_NAME_SIZE)) {
        Status = STATUS_MORE_PROCESSING_REQUIRED;
        goto done;
    }

    Status = STATUS_SUCCESS;
    if (!HostShareIsConnected(&pNetRootExt->Share)) {
        Status = HostConnectShare(pDevExt, &pNetRootExt->RootName, &pNetRootExt->Share);
    }

done:
    // update the net root state to be good.
    if (NT_SUCCESS(Status)) {
        pNetRoot->MRxNetRootState = MRX_NET_ROOT_STATE_GOOD;
        pNetRoot->DeviceType = RxDeviceType(DISK);
        pNetRoot->Type = NET_ROOT_DISK;
    }

    pCreateNetRootContext->VirtualNetRootStatus = Status;
    if (!pNetRootExt->fInitialized) {
        pCreateNetRootContext->NetRootStatus = Status;
    } else {
        pCreateNetRootContext->NetRootStatus = STATUS_SUCCESS;
    }

    // Callback the RDBSS for resumption.
    pCreateNetRootContext->Callback(pCreateNetRootContext);

    HWFS_DBG_PRINT(NT_SUCCESS(Status) ? DBG_INFO : DBG_WARN, DBG_SESSION,
        ("HwFsCreateVNetRoot status: 0x%x", Status));

    return STATUS_PENDING; // Always return pending!
}

NTSTATUS
HwFsFinalizeVNetRoot(
    IN PMRX_V_NET_ROOT pVNetRoot,
    IN PBOOLEAN        ForceDisconnect)
/*++

Arguments:

    pVNetRoot - the virtual net root

    ForceDisconnect - disconnect is forced

Return Value:

    NTSTATUS - The return status for the operation

--*/
{
    NTSTATUS Status;

    HWFS_DBG_PRINT(DBG_INFO, DBG_SESSION, ("HwFsFinalizeVNetRoot 0x%lx\n", pVNetRoot));

    return STATUS_SUCCESS;
}


NTSTATUS
HwFsFinalizeNetRoot(
    IN PMRX_NET_ROOT   pNetRoot,
    IN PBOOLEAN        ForceDisconnect)
{
    //
    //  This is called when all outstanding handles on this
    //  root have been cleaned up ! We can now zap the netroot
    //  extension...
    //
    PHWFS_DEVICE_EXTENSION  pDevExt = HWFS_GET_DEVICE_EXTENSION(pNetRoot->pSrvCall);
    PHWFS_NETROOT_EXTENSION pNetRootExt = HWFS_GET_NETROOT_EXTENSION(pNetRoot);

    HWFS_DBG_PRINT(DBG_INFO, DBG_SESSION, ("HwFsFinalizeNetRoot %p\n", pNetRoot));
    HostDisconnectShare(pDevExt, &pNetRootExt->Share);

    pNetRootExt->fInitialized = FALSE;

    return STATUS_SUCCESS;
}

VOID
HwFsExtractNetRootName(
    IN PUNICODE_STRING FilePathName,
    IN PMRX_SRV_CALL   SrvCall,
    OUT PUNICODE_STRING NetRootName,
    OUT PUNICODE_STRING RestOfName OPTIONAL)
{
    // Parse the input name into srv, netroot, and the rest
    UNICODE_STRING localRestOfName;

    ULONG length = FilePathName->Length;
    PWCHAR w = FilePathName->Buffer;
    PWCHAR wlimit = (PWCH)(((PCHAR)w) + length);
    PWCHAR wlow;

    HWFS_DBG_PRINT(DBG_INFO, DBG_SESSION, ("HwFsExtractNetRootName\n"));

    w += SrvCall->pSrvCallName->Length / sizeof(WCHAR);
    NetRootName->Buffer = wlow = w;
    for (;;) {
        if (w >= wlimit) {
            break;
        }
        if ((*w == OBJ_NAME_PATH_SEPARATOR) && (w != wlow)) {
            break;
        }
        w++;
    }

    NetRootName->Length = NetRootName->MaximumLength
                = (USHORT)((PCHAR)w - (PCHAR)wlow);

    if (!RestOfName) {
        RestOfName = &localRestOfName;
    }
    RestOfName->Buffer = w;
    RestOfName->Length = (USHORT)RestOfName->MaximumLength
                       = (USHORT)((PCHAR)wlimit - (PCHAR)w);

    HWFS_DBG_PRINT(DBG_INFO, DBG_SESSION, ("  HwFsExtractNetRootName FilePath=%wZ\n", FilePathName));
    HWFS_DBG_PRINT(DBG_INFO, DBG_SESSION, ("         Srv=%wZ,Root=%wZ,Rest=%wZ\n",
                        SrvCall->pSrvCallName,NetRootName,RestOfName));
}
